package org.dyndns.opendemogroup.optimizer.problems;

import static org.junit.Assert.*;

import org.junit.Test;

/**
 * A class to test the class LowAutocorrelationBinarySequence
 */
public class LowAutocorrelationBinarySequenceTest
{

	// { N, H, RLE sequence
	private Object[][] KnownSolutions =
	{
		{
			3, 1.0, "21"
		},
		{
			4, 2.0, "211"
		},
		{
			5, 2.0, "311"
		},
		{
			6, 7.0, "1113"
		},
		{
			7, 3.0, "1123"
		},
		{
			8, 8.0, "12113"
		},
		{
			9, 12.0, "42111"
		},
		{
			10, 13.0, "22114"
		},
		{
			11, 5.0, "112133"
		},
		{
			12, 10.0, "1221114"
		},
		{
			13, 6.0, "5221111"
		},
		{
			14, 19.0, "2221115"
		},
		{
			15, 15.0, "52221111"
		},
		{
			16, 24.0, "225111121"
		},
		{
			17, 32.0, "252211121"
		},
		{
			18, 25.0, "441112221"
		},
		{
			19, 29.0, "4111142212"
		},
		{
			20, 26.0, "5113112321"
		},
		{
			21, 26.0, "27221111121"
		},
		{
			22, 39.0, "51221111233"
		},
		{
			23, 47.0, "212121111632"
		},
		{
			24, 36.0, "2236111112121"
		},
		{
			25, 36.0, "337111121221"
		},
		{
			26, 45.0, "21212111116322"
		},
		{
			27, 37.0, "34313131211211"
		},
		{
			28, 50.0, "34313131211212"
		},
		{
			29, 62.0, "212112131313431"
		},
		{
			30, 59.0, "551212111113231"
		},
		{
			31, 67.0, "7332212211112111"
		},
		{
			32, 64.0, "71112111133221221"
		},
		{
			33, 64.0, "742112111111122221"
		},
		{
			34, 65.0, "842112111111122221"
		},
		{
			35, 73.0, "7122122111121111332"
		},
		{
			36, 82.0, "3632311131212111211"
		},
		{
			37, 86.0, "844211211111122221"
		},
		{
			38, 87.0, "8442112111111122221"
		},
		{
			39, 99.0, "82121121234321111111"
		},
		{
			40, 108.0, "44412112131121313131"
		},
		{
			41, 108.0, "343111111222281211211"
		},
		{
			42, 101.0, "313131341343112112112"
		},
		{
			43, 109.0, "1132432111117212112213"
		},
		{
			44, 122.0, "525313113111222111211121"
		},
		{
			45, 118.0, "82121121231234321111111"
		},
		{
			46, 131.0, "823431231211212211111111"
		},
		{
			47, 135.0, "923431231211212211111111"
		},
		{
			48, 140.0, "3111111832143212221121121"
		},
	};

	// }

	/**
	 * Tests the <i>computeFitness</i> method with the known perfect solutions.
	 */
	@Test
	public void computeFitness_DataDriven_KnownSolutions ( )
	{
		for ( int i = 0; i < KnownSolutions.length; i++ )
		{
			int inputLength = (int) (Integer) KnownSolutions[i][0];
			double expectedFitness = (double) (Double) KnownSolutions[i][1];
			String encodedInput = (String) KnownSolutions[i][2];
			computeFitnessAssert ( inputLength, encodedInput, expectedFitness );
		}
	}

	/**
	 * Tests the <i>computeFitness</i> method with the example provided at this
	 * page: <a href="http://www.cecm.sfu.ca/~jknauer/labs/">
	 * http://www.cecm.sfu.ca/~jknauer/labs/ </a>
	 */
	@Test
	public void computeFitness_sfu_website ( )
	{
		computeFitnessAssert ( 5, "1121", 10 );
	}

	/**
	 * Convenience method to make testing the <i>computeFitness</i> method
	 * easier.
	 * 
	 * @param inputLength
	 *        The member size, which is known as N for LABS.
	 * @param encodedInput
	 *        The Run-Length-Encoded version of the solution.
	 * @param expectedFitness
	 *        The fitness (i.e. energy) expected from the encodedInput.
	 */
	private void computeFitnessAssert ( int inputLength, String encodedInput,
			double expectedFitness )
	{
		LowAutocorrelationBinarySequence instance =
			new LowAutocorrelationBinarySequence ( inputLength );
		instance.decodeFrom ( encodedInput );
		double actualFitness = instance.computeFitness ( null );
		final String errorMessage =
			"The known solution for size " + inputLength
					+ " does not match expected solution.";
		assertEquals ( errorMessage, expectedFitness, actualFitness );
	}

	// { some data to drive the decodeFrom tests
	private Object[][] decodeTests =
	{
		{
			"21", new double[]
			{
				1, 1, -1
			}
		},
		{
			"5221111", new double[]
			{
				1, 1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1
			}
		},
	};

	// }

	/**
	 * Tests the <i>decodeFrom</i> method with some of the known solutions.
	 */
	@Test
	public void decodeFrom_DataDriven_SubsetOfKnownSolutions ( )
	{
		for ( int i = 0; i < decodeTests.length; i++ )
		{
			String input = (String) decodeTests[i][0];
			double[] expectedOutput = (double[]) decodeTests[i][1];
			decodeFromAssert ( input, expectedOutput );
		}
	}

	/**
	 * Convenience method to test the <i>decodeFrom</i> method.
	 * 
	 * @param input
	 *        The RLE version of the sequence of +1s and -1s.
	 * @param expectedOutput
	 *        An array of integers representing the decoded sequence.
	 */
	private void decodeFromAssert ( String input, double[] expectedOutput )
	{
		final int memberSize = expectedOutput.length;
		LowAutocorrelationBinarySequence instance =
			new LowAutocorrelationBinarySequence ( memberSize );
		instance.decodeFrom ( input );
		for ( int i = 0; i < expectedOutput.length; i++ )
		{
			final String errorMessage =
				"The input '" + input
						+ "' did not decode as expected at index " + i + ".";
			double expected = expectedOutput[i];
			double actual = instance.s[i];
			assertEquals ( errorMessage, expected, actual );
		}
	}
}
